package com.spring.cas.config;

import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.validation.Cas30ServiceTicketValidator;
import org.jasig.cas.client.validation.TicketValidator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;

import java.util.Arrays;

/**
 * @description:
 * @author: lfc
 * @version: 2020/7/22 14:32
 */
@Configuration
public class CasSecurityConfig {
    @Value("${cas.server.prefix}")
    private String casServerPrefix;
    @Value("${cas.server.login}")
    private String casServerLogin;
    @Value("${cas.server.logout}")
    private String casServerLogout;
    @Value("${cas.client.prefix}")
    private String casClientPrefix;
    @Value("${cas.client.login-process-url}")
    private String casClientLoginProcessUrl;
    @Value("${cas.client.login}")
    private String casClientLogin;
    @Value("${cas.client.logout.ab}")
    private String casClientLogout;
    @Value("${cas.client.logout.relative}")
    private String casClientLogoutRelative;
    @Value("${cas.user.inmemory}")
    private String casUserInMemory;

    @Bean
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        //与CasAuthenticationFilter,此参数用于重定向到cas登录时的service参数
        //如 http://192.168.4.40/cas/login?service=http://lcoalhost:8080/demo/login/cas
        //其中service中的login/cas需要和casAuthenticationFilter的setFilterProcessesUrl一致
        serviceProperties.setService(casClientLogin);
        //是否关闭单点登录，默认为false
        serviceProperties.setSendRenew(false);
        return serviceProperties;
    }

    /**
     * @description  cas 验证入口，提供用户浏览器重定向
     * @author lfc@2020/7/22 15:08
     * @param sp
     * @return org.springframework.security.web.AuthenticationEntryPoint
    */
    @Bean
    @Primary
    public AuthenticationEntryPoint authenticationEntryPoint(
            ServiceProperties sp) {

        CasAuthenticationEntryPoint entryPoint
                = new CasAuthenticationEntryPoint();
        //cas server认证登录的地址
        entryPoint.setLoginUrl(casServerLogin);
        entryPoint.setServiceProperties(sp);
        return entryPoint;
    }

    /**
     * @description  ticket校验
     * @author lfc@2020/7/22 15:11
     * @param
     * @return org.jasig.cas.client.validation.TicketValidator
    */
    @Bean
    public TicketValidator ticketValidator() {
        return new Cas30ServiceTicketValidator(casServerPrefix);
    }

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();

        manager.createUser(User.withUsername(casUserInMemory).password("").roles("USER").build());

        return manager;
    }

    /**
     * @description cas验证处理逻辑
     * @author lfc@2020/7/22 15:11
     * @param   
     * @return org.springframework.security.cas.authentication.CasAuthenticationProvider
    */
    @Bean
    public CasAuthenticationProvider casAuthenticationProvider() {
        CasAuthenticationProvider provider = new CasAuthenticationProvider();
        provider.setServiceProperties(serviceProperties());
        provider.setTicketValidator(ticketValidator());
        provider.setUserDetailsService(userDetailsService());
        provider.setKey("bluroo");

        return provider;
    }

    /**
     * @description  cas验证过滤器
     * @author lfc@2020/7/22 15:35
     * @param serviceProperties
     * @param authenticationProvider
     * @return org.springframework.security.cas.web.CasAuthenticationFilter
    */
    @Bean
    public CasAuthenticationFilter casAuthenticationFilter(ServiceProperties serviceProperties, AuthenticationProvider authenticationProvider) {
        CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();

        casAuthenticationFilter.setServiceProperties(serviceProperties);
        casAuthenticationFilter.setAuthenticationManager(new ProviderManager(Arrays.asList(authenticationProvider)));
        casAuthenticationFilter.setFilterProcessesUrl(casClientLoginProcessUrl);
        return casAuthenticationFilter;
    }

    @Bean
    public SingleSignOutFilter singleSignOutFilter() {
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();

        singleSignOutFilter.setCasServerUrlPrefix(casServerPrefix);
        singleSignOutFilter.setIgnoreInitConfiguration(true);

        return singleSignOutFilter;
    }


    /**
     * @description  登出操作，先本地处理登出，再跳转到cas进行登出
     * @author lfc@2020/7/29 10:00
     * @param   
     * @return org.springframework.security.web.authentication.logout.LogoutFilter
    */
    @Bean
    public LogoutFilter logoutFilter() {
        //targerUrl要加上service=http://XXXX,这样casserver登出后会跳转到servcie指定的地址
        String tragetUrl = casServerLogout;
        LogoutFilter logoutFilter = new LogoutFilter(tragetUrl,new SecurityContextLogoutHandler());

        logoutFilter.setFilterProcessesUrl(casClientLogoutRelative);

        return logoutFilter;
    }
}
